package com.mes.cloud.base;


import com.alibaba.fastjson.JSON;
import com.mes.cloud.commons.Constants;
import com.mes.cloud.commons.RequestUtils;
import lombok.Data;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.util.WebUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Data
public class ExceptionContext {

    private HttpServletRequest request;

    private HttpServletResponse response;

    private Throwable throwable;

    private String url;

    private String referer;

    private String contextPath;

    private RequestType type;

    private StringBuilder dumpInfo;

    public enum RequestType {
        JAVASCRIPT,
        HTML,
        JSON
    }

    private String getContextPath(HttpServletRequest request) {
        String uri = (String) request.getAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE);
        return uri != null && !"/".equals(uri) ? StringUtils.tokenizeToStringArray(uri, "/")[0] : "";
    }

    private String getCurrentUrl(HttpServletRequest request) {
        String uri = (String) request.getAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE);
        return uri != null ? uri : "";
    }

    public ExceptionContext(HttpServletRequest request, HttpServletResponse response, Throwable error) {
        this(request, response, error, false);
    }

    public ExceptionContext(HttpServletRequest request, HttpServletResponse response, Throwable error,
                            boolean redirectToError) {
        this.request = request;
        this.response = response;

        while (error != null && error.getCause() != null) {
            error = error.getCause();
        }
        this.throwable = error;

        this.url = redirectToError ? getCurrentUrl(request) : request.getRequestURL().toString();

        this.referer = request.getHeader(HttpHeaders.REFERER);

        this.contextPath = redirectToError ? getContextPath(request) : request.getContextPath();

        String accept = request.getHeader(HttpHeaders.ACCEPT);
        accept = accept == null ? "" : accept.toLowerCase();

        if (accept.contains("json")) {

            this.type = RequestType.JSON;

        } else if (this.url.toLowerCase().endsWith(".cjs") || accept.contains("javascript")) {

            this.type = RequestType.JAVASCRIPT;

        } else if (accept.contains("text/html") || this.url.toLowerCase().endsWith(".html")
                || this.url.toLowerCase().contains(".html#/")) {

            this.type = RequestType.HTML;

        } else {

            this.type = RequestType.JSON;

        }

        this.dumpInfo = RequestUtils.dumpRequest(request, response);
    }

    public String getRedirectUrl() {
        if (RequestType.HTML == type) {
            return url;
        }

        return StringUtils.hasText(referer) ? referer : "/";
    }

    public String getBriefThrowableMessage() {
        return throwable == null ? "UNKNOWN ERROR." : throwable.getClass().getSimpleName();
        //String.format("%s: %s", throwable.getClass().getSimpleName(), throwable.getMessage());
    }

    public void sendRedirect(String url) throws IOException {
        response.sendRedirect(url);
    }

    public void writeJavaScriptToResponse(HttpStatus status, CharSequence charSequence) throws IOException {
        ResponseEntity<String> entity = ResponseEntity.status(status).headers(WebConstants.NO_CACHE_HEADERS)
                .contentType(WebConstants.X_JAVASCRIPT_TYPE)
                .body(charSequence.toString());
        writeToResponse(entity);
    }

    public void writeJsonToResponse(HttpStatus status, Object jsonObject) throws IOException {
        ResponseEntity<String> entity = ResponseEntity.status(status)
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .body(JSON.toJSONString(jsonObject));
        writeToResponse(entity);
    }

    public void writeToResponse(ResponseEntity<String> entity) throws IOException {
        ServletServerHttpResponse res = new ServletServerHttpResponse(this.response);

        HttpHeaders entityHeaders = entity.getHeaders();
        if (!entityHeaders.isEmpty()) {
            res.getHeaders().putAll(entityHeaders);
        }

        StreamUtils.copy(entity.getBody(), Constants.UTF8, res.getBody());
    }

}
